#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_sInst;

// 获取一个非空的span
Span* CentralCache::GetOneSpan(SpanList &list, size_t size)
{
    // 查看当前spanlist中是否还有未分配对象的span
    Span* it = list.Begin();
    while (it != list.End())
    {
        if (it->_freeList != nullptr)
        {
            return it;
        }
        else
        {
            it = it->_next;
        }
    }

    // 先把central cache的桶锁解掉，这样如果其它线程释放内存对象时，不会阻塞
    list.mtx.unlock();


    // 走到这里说明没有空闲的span了，只能找pagecache要
    PageCache::GetInstance()->_pageMtx.lock();
    Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
    PageCache::GetInstance()->_pageMtx.unlock();


    // 对span进行切分，不用加锁，因为其它线程访问不到这个span

    // 计算span的大块内存的起始地址和大块内存的大小(字节数)
    char* start = (char*)(span->_pageId << PAGE_SHIIFT);
    size_t bytes = span->_n << PAGE_SHIIFT; // 页的数量乘8kb
    char* end = start + bytes;

    // 把大块内存切成自由链表链接起来
    // 1、先切一块下来做头，方便尾插

    span->_freeList = start;
    start += size;
    void* tail = span->_freeList;

    // 尾插
    int i = 1;
    while (start < end)
    {
        i++;
        NextObj(tail) = start;
        tail = NextObj(tail);
        start += size;
    }


    // 切好span以后，需要把span挂到桶里面去的时候，再加锁
    list.mtx.lock();
    list.PushFront(span);

    return span;
}

// 从中心缓存中获取一定量的对象给thread cache
size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanlists[index].mtx.lock();

    Span* span = GetOneSpan(_spanlists[index], size);
    assert(span);
    assert(span->_freeList);

    // 从span中获取batchNum个对象
    // 如果不够batchNum个，有多少拿多少
    start = span->_freeList;
    end = start;

    size_t i = 0;
    size_t actualNum = 1;

    while (i < batchNum - 1 && NextObj(end) != nullptr)
    {
        end = NextObj(end);
        ++i;
        ++actualNum;
    }

    span->_freeList = NextObj(end);
    NextObj(end) = nullptr;

    _spanlists[index].mtx.unlock();

    return actualNum;
}